/*
有 n 个城市，其中一些彼此相连，另一些没有相连。如果城市 a 与城市 b 直接相连，且城市 b 与城市 c 直接相连，那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市，组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ，其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连，而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

 

示例 1：


输入：isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出：2
示例 2：


输入：isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出：3
 

提示：

1 <= n <= 200
n == isConnected.length
n == isConnected[i].length
isConnected[i][j] 为 1 或 0
isConnected[i][i] == 1
isConnected[i][j] == isConnected[j][i]
*/

#include "stdc++.h"

/* 深度优先搜索
深度优先搜索的思路是很直观的。遍历所有城市，对于每个城市，如果该城市尚未被访问过，则从该城市开始深度优先搜索，
通过矩阵 isConnected 得到与该城市直接相连的城市有哪些，这些城市和该城市属于同一个连通分量，然后对这些城市继续深度优先搜索，
直到同一个连通分量的所有城市都被访问到，即可得到一个省份。遍历完全部城市以后，即可得到连通分量的总数，即省份的总数。
*/
class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        int n = isConnected.size();
        vector<bool> visited(n, false);
        int res{0};
        for (int i{0}; i < n; ++i) {
            if (!visited[i]) {
                visited[i] = 1;
                dfs(isConnected, visited, n, i);
                ++res;
            }
        }
        return res;
    }
    void dfs(const vector<vector<int>>& isConnected, vector<bool>& visited, const int& n, int i) {
        for (int j{0}; j < n; ++j) {
            if (isConnected[i][j] == 1 && !visited[j]) {
                visited[j] = 1;
                dfs(isConnected, visited, n, j);
            }
        }
    }
};

/* 广度优先搜索
对于每个城市，如果该城市尚未被访问过，则从该城市开始广度优先搜索，直到同一个连通分量中的所有城市都被访问到，即可得到一个省份。
*/
class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        int n = isConnected.size();
        vector<bool> visited(n, false);
        int res{0};
        queue<int> q{};
        for (int k{0}; k < n; ++k) {
            if (!visited[k]) {
                q.push(k);
                while (!q.empty()) {
                    int i = q.front();
                    q.pop();
                    visited[i] = 1;
                    for (int j{0}; j < n; ++j) {
                        if (isConnected[i][j] == 1 && !visited[j]) {
                            q.push(j);
                        }
                    }
                }
                ++res;
            }
        }
        return res;
    }
};

/* 并查集
计算连通分量数的另一个方法是使用并查集。初始时，每个城市都属于不同的连通分量。
遍历矩阵 isConnected，如果两个城市之间有相连关系，则它们属于同一个连通分量，对它们进行合并。
遍历矩阵 isConnected 的全部元素之后，计算连通分量的总数，即为省份的总数。
*/
class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        int n = isConnected.size();
        vector<int> parent(n, 0);
        for (int i{0}; i < n; ++i) {
            parent[i] = i;
        }
        for (int i{0}; i < n; ++i) {
            for (int j{i + 1}; j < n; ++j) {
                if (isConnected[i][j] == 1) {
                    Union(parent, i, j);
                }
            }
        }
        int res{0};
        for (int i{0}; i < n; ++i) {
            if (parent[i] == i) {
                ++res;
            }
        }
        return res;
    }
    void Union(vector<int>& parent, int index1, int index2) {
        parent[Find(parent, index1)] = Find(parent, index2);
    }
    int Find(vector<int>& parent, int index) {
        if (parent[index] != index) {
            parent[index] = Find(parent, parent[index]);
        }
        return parent[index];
    }
};